/*
 * @author Chris
 * @since 2023/1/27 20:15
 *
 * @brief    This file sends or receives data through UART.
 *           First, save data to {@link bufRxd};
 *           Second, judgement data whether receive over or not, through {@link UartRxMonitor()} function by other timer.
 *
 *
 * @note     {@link ConfigUART()} config UART
 *           {@link UartWrite()} send data by uart.
 *           {@link UartRead()} receive data from buf.
 *           {@link UartRxMonitor()} receive data from buf.
 *           {@link UartDriver()} UART driver deal with extern UartAction() function.
 *
 * @warning  Do not to ...
 *
 * Change Logs:
 *   Date           Author       Notes
 *   2023/1/27     Chris        the first version
 */

#include "reg51.h"

__bit flagFrame = 0;        // 帧接收完成标志
__bit flagTxd = 0;          // 单字节发送完成标志
unsigned char cntRxd = 0;   // 接收字节计数器
unsigned char __pdata bufRxd[64];   // 接收字节缓冲区

extern void UartAction(unsigned char *buf, unsigned char len);

void ConfigUART(unsigned int baud) {
    SCON = 0x50;
    TMOD = TMOD & 0x0F | 0x20;
    TH1 = 256 - (11059200 / 12 / 2 / 16) / baud;
    TL1 = TH1;

    ET1 = 0;
    ES = 1;
    TR1 = 1;
}

void UartWrite(unsigned char *buf, unsigned char len) {
    while (len--) {
        do {
            flagTxd = 0;
            SBUF = *buf++;
        } while (!flagTxd);
    }
}

unsigned char UartRead(unsigned char *buf, unsigned char len) {
    len = len > cntRxd ? cntRxd : len;
    for (int i = 0; i < len; ++i) {
        *buf++ = bufRxd[i];
    }
    cntRxd = 0;
    return len;
}

/**
 * 串口接收监控，由空闲时间判定帧结束，需要在定时中断中调用
 *
 * @param ms 定时间隔
 */
void UartRxMonitor(unsigned char ms) {
    static unsigned char cntBak = 0;    // 接收计数器
    static unsigned char idleTimer = 0; // 累计空闲时间

    if (cntRxd <= 0) {
        cntBak = 0;
        return;
    }

    // 接收计数器大于 0，监控总线空闲时间，找到帧接收完成的节点
    if (cntBak != cntRxd) {
        cntBak = cntRxd;
        idleTimer = 0;
    } else {
        idleTimer += ms;
        flagFrame = idleTimer >= 30 ? 1 : flagFrame;
    }
}

/**
 * 串口驱动函数，监测数据帧接收，调度功能函数，在主循环中调用
 */
void UartDriver() {
    if (!flagFrame) {
        return;
    }
    flagFrame = 0;

    unsigned char __pdata buf[40];
    unsigned char len = UartRead(buf, sizeof(buf));
    UartAction(buf, len);
}

/**
 * 串口中断服务函数
 */
void InterruptUART() __interrupt(4) {
    if (RI) {
        RI = 0;
        if (cntRxd < sizeof(bufRxd)) {
            bufRxd[cntRxd++] = SBUF;
        }
    }

    if (TI) {
        TI = 0;
        flagTxd = 1;
    }
}